package com.ss.isc.util.netty;

import com.ss.isc.util.em.Enums;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CaptureMyChannelHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger LOGGER = LoggerFactory.getLogger(CaptureMyChannelHandler.class);

    private static WebSocketServerHandshaker handshaker;
    private static final String HOME_ADD = "/home";
    private static final int STATUS_CODE = 200;
    private static Map<String, List<ChannelHandlerContext>> CTX_MAP = new HashMap();


    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        CaptureGlobalUserUtil.channels.add(ctx.channel());
    }


    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        CaptureGlobalUserUtil.channels.remove(ctx);
    }


    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        ctx.channel().close();
    }


    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("【channelActive】=====>" + ctx.channel());
    }


    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    }


    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }


    protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof HttpRequest) {
            doHandlerHttpRequest(ctx, (HttpRequest) msg);


        } else if (msg instanceof WebSocketFrame) {
            doHandlerWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }


    public static void doHandlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame msg) {
        if (msg instanceof CloseWebSocketFrame) {

            handshaker.close(ctx.channel(), (CloseWebSocketFrame) msg);
            return;
        }
        if (msg instanceof PingWebSocketFrame) {


            PongWebSocketFrame pong = new PongWebSocketFrame(msg.content().retain());
            ctx.channel().writeAndFlush(pong);
            return;
        }
        if (msg instanceof PongWebSocketFrame) {


            PingWebSocketFrame ping = new PingWebSocketFrame(msg.content().retain());
            ctx.channel().writeAndFlush(ping);
            return;
        }
        if (!(msg instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException("不支持二进制");
        }


        if (ctx == null) ;


        for (Channel channel : CaptureGlobalUserUtil.channels) {

            ctx.channel().writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame) msg)
                    .text()));
        }
    }


    private void doHandlerHttpRequest(ChannelHandlerContext ctx, HttpRequest msg) {
        System.out.println("-------------------------msg.headers().get(\"Upgrade\"))--------------" + msg

                .headers().get("Validator"));
        if (!msg.getDecoderResult().isSuccess() ||
                !"websocket".equalsIgnoreCase(msg.headers().get("Upgrade"))) {
            sendHttpResponse(ctx, (FullHttpRequest) msg, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
        }


        String uri = msg.getUri();
        if (!uri.substring(1).equals(Enums.NettyUri.URI_HOME.getCode()) &&
                !uri.substring(1).equals(Enums.NettyUri.URI_OTHER.getCode())) {
            ctx.close();
        }
        ctx.attr(AttributeKey.valueOf("type")).set(uri);

        String flag = null;
        if (HOME_ADD.equals(uri)) {
            flag = Enums.NettyUri.URI_HOME.getCode();
        } else {

            flag = Enums.NettyUri.URI_OTHER.getCode();
        }


        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory("ws://" + msg.headers().get("Host") + "/" + flag + "", null, false);

        handshaker = factory.newHandshaker(msg);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        }

        handshaker.handshake(ctx.channel(), (FullHttpRequest) msg);

        if (CTX_MAP.containsKey(flag)) {
            ((List) CTX_MAP.get(flag)).add(ctx);
        } else {

            List<ChannelHandlerContext> ctxList = new ArrayList<ChannelHandlerContext>();
            ctxList.add(ctx);
            CTX_MAP.put(flag, ctxList);
        }
    }


    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        if (res.getStatus().code() != STATUS_CODE) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);

            res.content().writeBytes(buf);
            buf.release();
        }

        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != STATUS_CODE) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }


    public static void send(String msg, String upgrade) {
        if (CTX_MAP.containsKey(upgrade)) {
            List<ChannelHandlerContext> ctxList = (List) CTX_MAP.get(upgrade);
            TextWebSocketFrame tw = new TextWebSocketFrame(msg);
            for (ChannelHandlerContext ctx : ctxList) {
                ctx.channel().writeAndFlush(new TextWebSocketFrame(tw.text()));
            }
        } else {

            LOGGER.info("消息推送失败，未找到[" + upgrade + "]channel");
        }
    }

}
